#include <scheme.h>
#include <linux/version.h>
#include <linux/module.h>  
#include <linux/kernel.h>  
#include <linux/init.h>  
#include <net/sock.h>  
#include <linux/socket.h>  
#include <linux/net.h>  
#include <asm/types.h>  
#include <linux/netlink.h>  
#include <linux/skbuff.h>  
#include "net_link_io.h"

#define NETLINK_USER 31  

// ---------------------------------------------------------------------------------

struct __NETLINK_IO
{
	XOBJECT objHdr;
	struct sock * sk;
};

static void __destroyNetlinkIo(NETLINK_IO * io)
{
	if(io->sk != NULL){
		netlink_kernel_release(io->sk);
		io->sk = NULL;
	}
	kfree(io);
	return;
}
 
static NETLINK_IO * createNetlinkIo(void)
{
	NETLINK_IO * io = (NETLINK_IO *)kmalloc(sizeof(NETLINK_IO), GFP_KERNEL);
	
	if(!io){
		return NULL;
	}
	memset(io, 0, sizeof(NETLINK_IO));
	io->objHdr.destroyPtr = (DestroyAtomPtr) __destroyNetlinkIo;
	io->sk = NULL;
	xobject_addref(io);
	return io;
}

NETLINK_IO * netlinkIoBuild(unsigned int __prot,  void (* recv_msg_ptr)(struct sk_buff *))
{
	NETLINK_IO * io = NULL; 
	
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0) 
	struct netlink_kernel_cfg cfg = {
		.input = recv_msg_ptr,
	};
#endif

	io = createNetlinkIo();
	if(!io) goto DONE;
	
	
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0) 	
	io->sk = netlink_kernel_create(&init_net, __prot, &cfg);  
	printk("Entering: %s <%p>\n",__FUNCTION__, io->sk);  
	
#else
	printk("Entering: %s\n",__FUNCTION__);  
	io->sk = netlink_kernel_create(&init_net, __prot, 0,
		   recv_msg_ptr, NULL, THIS_MODULE);  	
#endif
	if(!io->sk){
		xobject_release(io);
		io = NULL;
		goto DONE;
	}
	
DONE:
	return io;
}

int get_netlink_payload(struct sk_buff * skb, int * pid, char ** ppData, int * pLength)
{
	struct nlmsghdr * nlh;  
	
	nlh = (struct nlmsghdr*)skb->data;  
	
	* pid = nlh->nlmsg_pid;
	* ppData = (char*)NLMSG_DATA(nlh);
	* pLength = NLMSG_PAYLOAD(nlh, 0);
	return 0;
}

struct sock * get_sock(NETLINK_IO * io)
{
	return io->sk;
}

int send_net_link_data(struct sock * sock, unsigned int portid, unsigned char * data, int length)
{
	int retval = -1;
	struct sk_buff * skb_out = NULL;
	struct nlmsghdr * nlh; 
	// -- struct netlink_sock * nlk;
	
	skb_out = nlmsg_new(length, 0);
	if(!skb_out){
		printk(KERN_ERR "Failed to allocate new skb\n");
		goto DONE;
	}	
	
	nlh = nlmsg_put(skb_out, 0, 0, NLMSG_DONE, length, 0);  
	NETLINK_CB(skb_out).dst_group = 0;  
	memcpy(nlmsg_data(nlh), (char * )data, length);
	// -- NETLINK_CB(skb).groups = 0; /* not in mcast group */  
	// -- NETLINK_CB(skb).dst_pid = pid;  
	
	// -- netlink_unicast(sock, skb, pid, MSG_DONTWAIT);  
	retval = nlmsg_unicast(sock, skb_out, portid);

	if(retval != 0){
		// -- #define	ECONNREFUSED	111	/* Connection refused */
		printk(KERN_INFO "[!] Error while sending back to user (length:%d) (%d)\n", length, retval);
		goto DONE;
	}else{
		// -- printk(KERN_INFO "[+] sending back to user successfully\n");
	}
	
	retval = 0;
DONE:	
	return retval;
}
 